#!/usr/bin/env python
#
# Copyright 2011-2016 The Rust Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution and at
# http://rust-lang.org/COPYRIGHT.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

# This script uses the following Unicode tables:
# - Categories.txt

import os
import subprocess

def to_ranges(iter):
    current = None
    for i in iter:
        if current is None or i != current[1] or i in (0x10000, 0x20000):
            if current is not None:
                yield tuple(current)
            current = [i, i + 1]
        else:
            current[1] += 1
    if current is not None:
        yield tuple(current)

def get_escaped(dictionary):
    for i in range(0x110000):
        if dictionary.get(i, "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and i != ord(' '):
            yield i

def get_file(f):
    try:
        return open(os.path.basename(f))
    except FileNotFoundError:
        subprocess.run(["curl", "-O", f], check=True)
        return open(os.path.basename(f))

def main():
    file = get_file("http://www.unicode.org/notes/tn36/Categories.txt")

    dictionary = {int(line.split()[0], 16): line.split()[1] for line in file}

    CUTOFF=0x10000
    singletons0 = []
    singletons1 = []
    normal0 = []
    normal1 = []
    extra = []

    for a, b in to_ranges(get_escaped(dictionary)):
        if a > 2 * CUTOFF:
            extra.append((a, b - a))
        elif a == b - 1:
            if a & CUTOFF:
                singletons1.append(a & ~CUTOFF)
            else:
                singletons0.append(a)
        elif a == b - 2:
            if a & CUTOFF:
                singletons1.append(a & ~CUTOFF)
                singletons1.append((a + 1) & ~CUTOFF)
            else:
                singletons0.append(a)
                singletons0.append(a + 1)
        else:
            if a >= 2 * CUTOFF:
                extra.append((a, b - a))
            elif a & CUTOFF:
                normal1.append((a & ~CUTOFF, b - a))
            else:
                normal0.append((a, b - a))

    print("""\
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// NOTE: The following code was generated by "src/etc/char_private.py",
//       do not edit directly!

use slice::SliceExt;

fn check(x: u16, singletons: &[u16], normal: &[u16]) -> bool {
    for &s in singletons {
        if x == s {
            return false;
        } else if x < s {
            break;
        }
    }
    for w in normal.chunks(2) {
        let start = w[0];
        let len = w[1];
        let difference = (x as i32) - (start as i32);
        if 0 <= difference {
            if difference < len as i32 {
                return false;
            }
        } else {
            break;
        }
    }
    true
}

pub fn is_printable(x: char) -> bool {
    let x = x as u32;
    let lower = x as u16;
    if x < 0x10000 {
        check(lower, SINGLETONS0, NORMAL0)
    } else if x < 0x20000 {
        check(lower, SINGLETONS1, NORMAL1)
    } else {\
""")
    for a, b in extra:
        print("        if 0x{:x} <= x && x < 0x{:x} {{".format(a, a + b))
        print("            return false;")
        print("        }")
    print("""\
        true
    }
}\
""")
    print()
    print("const SINGLETONS0: &'static [u16] = &[")
    for s in singletons0:
        print("    0x{:x},".format(s))
    print("];")
    print("const SINGLETONS1: &'static [u16] = &[")
    for s in singletons1:
        print("    0x{:x},".format(s))
    print("];")
    print("const NORMAL0: &'static [u16] = &[")
    for a, b in normal0:
        print("    0x{:x}, 0x{:x},".format(a, b))
    print("];")
    print("const NORMAL1: &'static [u16] = &[")
    for a, b in normal1:
        print("    0x{:x}, 0x{:x},".format(a, b))
    print("];")

if __name__ == '__main__':
    main()
